<template>
    <div class="file-tree-container">
        <el-tree lazy :data="fileStore.fileTreeList" :props="defaultProps" :default-expanded-keys="expandedFileTree"
                 empty-text="暂无数据" @node-click="handleNodeClick" :load="loadFolder" @node-expand="handleNodeExpand"
                 @node-collapse="handleNodeCollapse">
            <template #default="{ node, data }">
                <div class="custom-tree-node">
                    <div class="tree-label" :title="node.label">{{ node.label }}</div>
                    <div class="tree-btns">
                        <el-button v-show="data.isDirectory" type="primary" link :icon="Plus"
                                   @click.stop="handleAdd(node, data)"></el-button>
                        <el-button type="danger" link :icon="Delete" @click.stop="handleDelete(node, data)"></el-button>
                    </div>
                </div>
            </template>
        </el-tree>
    </div>
</template>

<script setup>
import {ref} from "vue";
import {Delete, Plus} from "@element-plus/icons-vue";
import {useFileStore} from "../store/fileStore.js";
import {storeToRefs} from "pinia";
import {readDir, readTextFile} from "@tauri-apps/plugin-fs";
import {listToTree} from "../utils/utils.js";
import {ElMessageBox} from "element-plus";
import {deleteFile, updateFileList} from "../utils/clientControl.js";
import {useDebounceFn} from '@vueuse/core';

const fileStore = useFileStore();
const {expandedFileTree} = storeToRefs(fileStore);

const emit = defineEmits(["selectedFile", "crateNewFile"]);

const defaultProps = ref({
    children: "children",
    label: "label",
    isLeaf: "leaf"
});

// 需要展开的树节点
const expandedKeys = ref([]);

/** 文件/文件夹选中方法 */
async function handleNodeClick(data) {
    if (!data.isFile) {
        return;
    }
    fileStore.setSelectFileAddress(data.path);
    // 读取文件数据(必须在这里操作，防止异步问题)
    const fileText = await readTextFile(data.path);
    fileStore.setFileText(fileText);
    emit("selectedFile");
}

/** 懒加载 */
async function loadFolder(node, resolve) {
    if (node.level === 0) {
        return resolve([]);
    } else {
        const folderAddress = node.data.path;
        const folderDir = await readDir(folderAddress);
        const treeData = listToTree(folderDir, folderAddress);
        resolve(treeData);
    }
}

/** 树节点展开方法 */
function handleNodeExpand(data, node, even) {
    debouncedHandleNodeExpandOrCollapse('expand', data);
}

/** 树节点收起方法 */
function handleNodeCollapse(data, node, even) {
    debouncedHandleNodeExpandOrCollapse('collapse', data);
}

/**
 * 创建防抖函数，树节点展开收起
 * */
const debouncedHandleNodeExpandOrCollapse = useDebounceFn((type, data) => {
    // 处理节点展开或收起逻辑
    if (type === 'expand') {
        expandedKeys.value.push(data.$treeNodeId);
    } else if (type === 'collapse') {
        expandedKeys.value = expandedKeys.value.filter(item => item !== data.path);
    }
    fileStore.setExpandedFileTree(expandedKeys.value);
}, 200);

/** 新增方法 */
async function handleAdd(node, data) {
    console.log(node, data)
    emit("crateNewFile", {path: data.path});
}

/** 删除方法 */
async function handleDelete(node, data) {
    console.log(node, data)
    const title = data.isFile ? "文件" : "文件夹";
    const desc = data.isFile ? "" : "请注意：删除文件夹后其内部文件一并被删除！";
    ElMessageBox.confirm(`你确定要删除 '${data.label} ${title}' 吗？ ${desc}`, '操作提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
    }).then(async () => {
        await deleteFile(data.path);
        await updateFileList(fileStore.fileRootPath);
    });
}

defineExpose({
    handleNodeClick,
})
</script>

<style scoped lang="less">
.file-tree-container {
    padding: 10px;
    box-sizing: border-box;
    overflow: auto;
}

.custom-tree-node {
    width: calc(100% - 24px);
    display: flex;
    align-items: center;
    justify-content: space-between;
    font-size: 14px;

    .tree-label {
        width: calc(100% - 20px);
        flex: 1;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    .tree-btns > .el-button:last-child {
        margin: 0;
    }
}
</style>